package ru.fadeev.crammer;

import ru.fadeev.crammer.data.DictionariesManager;
import ru.fadeev.crammer.data.Dictionary;
import ru.fadeev.crammer.data.Sheet;
import ru.fadeev.crammer.data.WordRecord;


/**
 * TestNeedsWatcher class is process that determines test needs of words.
 * When Crammer starts it begins to check test needs for every word for every
 * test type. Then TestNeedsWatcher wakes up every 'testNeedsCheckPeriod' 
 * (customized in Settings) and does full recheck.
 * @author Konstantin Fadeyev
 *
 */
public class TestNeedsWatcher extends Thread {
	
	protected DictionariesManager dictionariesManager;
	protected boolean checkAllAgain;
	private volatile boolean interrupted;
	private TestNeedsListener testNeedsListener;


	
	/**
	 * Create instance of TestNeedsWatcher
	 * @param dictionariesManager object of DictionariesManager
	 */
	public TestNeedsWatcher(DictionariesManager dictionariesManager) {
		this.dictionariesManager = dictionariesManager;
		checkAllAgain = false;
		interrupted = false;
		testNeedsListener = null;
	}

	
	/**
	 * Request to TestNeedsWatcher for full recheck of words test needs.
	 */
	public synchronized void recheck() {
		checkAllAgain = true;
		notify();
	}
    
	/**
	 * Checks test needs for every word for every test type. 
	 * Then TestNeedsWatcher wakes up every 'testNeedsCheckPeriod' 
	 * (customized in Settings) and does full recheck.
	 */
	public void run() {
		long timeToCheckAgain = System.currentTimeMillis() + 
								Settings.getLong("testNeedsCheckPeriod");
		while(!interrupted) {
			int i = 0;
			int j = 0;
			int k = 0;
			while(!interrupted) {
				synchronized(dictionariesManager) {
					if(i >= dictionariesManager.size()) {
						break;
					}
					Dictionary dictionary = dictionariesManager.getDictionary(i);
					synchronized(dictionary) {
						if (j >= dictionary.size()) {
							j = 0;
							i++;
							continue;
						}
						Sheet sheet = dictionary.getSheet(j);
						synchronized(sheet) {
							if (k >= sheet.size()) {
								k = 0;
								j++;
								continue;
							}
							WordRecord wordRecord = sheet.getWordRecord(k);
							for (int l = 0; l < TestManager.TESTS_NUMBER; l++) {
								wordRecord.refreshNeededToTest(l, i, j);
							}
							k++;
						}
					}
				}	
			}
			if(interrupted) {
				break;
			}
			if(Settings.getBoolean("showNeededToTestWindow")) {
				int max = 0;
				int maxTestType = 0;
				int neededToTest = Settings.getInt("neededToTestWindowNumber");
				for(int l = 0; l < TestManager.TESTS_NUMBER; l++) {
					int testCount = dictionariesManager.getTestWordsCount(l);
					if(testCount >= neededToTest && testCount > max) {
						max = testCount;
						maxTestType = l;
					}
				}
				if(max > 0) {	
					if(testNeedsListener != null) {
						testNeedsListener.needToTest(maxTestType);
					}
				}
			}
			try {
				synchronized(this) {
					if(checkAllAgain) {
						checkAllAgain = false;
					} else {					
						if(timeToCheckAgain < System.currentTimeMillis()) {
							timeToCheckAgain = System.currentTimeMillis() 
								+ Settings.getLong("testNeedsCheckPeriod");
						}
						long timeToWait = 
							timeToCheckAgain - System.currentTimeMillis();
						wait(timeToWait);
						checkAllAgain = false;
					}
				}
			} catch(InterruptedException e) {	
				return;
			}
		}
	}
	
	
	/**
	 * Sets new TestNeedsListener
	 * @param listener new listener
	 */
	public void setTestNeedsListener(TestNeedsListener listener) {
		testNeedsListener = listener;
	}
	
	
	/**
	 * Overwritten so volatile variable that is used to stop process is initialized.
	 */
    public void start() {
        interrupted = false;
        super.start();
    }
	
	
	/**
	 * Stops process. Function waits till it stops.
	 */
	public void stopWatcher() {
		interrupted = true;
		interrupt();
		try {
			this.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
